Example from Section 21.5.4.1 (page 441) in K20P64M72SF1RM manual (MK20DX128 processor used on Teensy 3.1/3.2).

From the manual:

...the following TCD entry is configured to transfer 16 bytes of data. The eDMA is programmed for one iteration of the major loop transferring 16 bytes per iteration. The source memory has a byte wide memory port located at 0x1000. The destination memory has a 32-bit port located at 0x2000. The address offsets are programmed in increments to match the transfer size: one byte for the source and four bytes for the destination. The final source and destination addresses are adjusted to return to their beginning values.

TCDn_CITER = TCDn_BITER = 1
TCDn_NBYTES = 16
TCDn_SADDR = 0x1000
TCDn_SOFF = 1
TCDn_ATTR[SSIZE] = 0
TCDn_SLAST = -16
TCDn_DADDR = 0x2000
TCDn_DOFF = 4
TCDn_ATTR[DSIZE] = 2
TCDn_DLAST_SGA= –16
TCDn_CSR[INT_MAJ] = 1
TCDn_CSR[START] = 1 (Should be written last after all other fields have been initialized)
All other TCDn fields = 0

This generates the following event sequence:

  1. User write to the TCDn_CSR[START] bit requests channel service.
  2. The channel is selected by arbitration for servicing.
  3. eDMA engine writes: TCDn_CSR[DONE] = 0, TCDn_CSR[START] = 0, TCDn_CSR[ACTIVE] = 1.

Connect to device


In [1]:
import numpy as np
from teensy_minimal_rpc import SerialProxy
import teensy_minimal_rpc.DMA as dma

# Disconnect from existing proxy (if available)
try:
    del proxy
except NameError:
    pass

proxy = SerialProxy()

Allocate two arrays: source and destination


In [2]:
N = 512
proxy.free_all()

# Allocate source array
src_addr = proxy.mem_alloc(N)
# Allocate destination array
dst_addr = proxy.mem_alloc(N)

# Fill first 16 bytes of source array with the numbers 1-16
proxy.mem_cpy_host_to_device(src_addr, np.arange(1, 17, dtype='uint8'))

# Fill the destination array with all zeros
proxy.mem_fill_uint32(dst_addr, 0, N / 4)

Create Transfer Control Descriptor (TCD) configuration


In [3]:
# Create Transfer Control Descriptor configuration to match the settings
# shown in the example from the manual.
XFER_REQUEST = dma.TCD(
    # TCDn_CITER = TCDn_BITER = 1
    CITER_ELINKNO=dma.R_TCD_ITER_ELINKNO(ITER=1),
    BITER_ELINKNO=dma.R_TCD_ITER_ELINKNO(ITER=1),
    # TCDn_NBYTES = 16
    NBYTES_MLNO=16,
    # TCDn_SADDR = 0x1000
    SADDR=int(src_addr),
    # TCDn_SOFF = 1
    SOFF=1,
    # TCDn_ATTR[SSIZE] = 0
    # See `TCDn_ATTR[DSIZE]` below.
    # TCDn_SLAST = -16
    SLAST=-16,
    # TCDn_DADDR = 0x2000
    DADDR=int(dst_addr),
    # TCDn_DOFF = 4
    DOFF=4,
    # TCDn_ATTR[DSIZE] = 2
    ATTR=dma.R_TCD_ATTR(SSIZE=dma.R_TCD_ATTR._8_BIT,
                        DSIZE=dma.R_TCD_ATTR._32_BIT),
    # TCDn_DLAST_SGA= –16
    DLASTSGA=-16,
    # TCDn_CSR[INT_MAJ] = 1
    # We won't use interrupts here...
    # TCDn_CSR[START] = 1 (Should be written last after all other fields have been initialized)
    CSR=dma.R_TCD_CSR(START=1)
    # All other TCDn fields = 0
)

Apply Transfer Control Descriptor configuration to start transfer


In [4]:
print 'SOURCE:  ', proxy.mem_cpy_device_to_host(src_addr, 16)

# Fill the destination array with all zeros
proxy.mem_fill_uint32(dst_addr, 0, N / 4)
print 'TARGET:'
print '  Before:', proxy.mem_cpy_device_to_host(dst_addr, 16)

# Apply TCD configuration to DMA channel 0 to conduct transfer.
proxy.update_dma_TCD(0, XFER_REQUEST)

print '   After:', proxy.mem_cpy_device_to_host(dst_addr, 16)


SOURCE:   [ 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16]
TARGET:
  Before: [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
   After: [ 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16]

Confirm transfer completed successfully (i.e., check DMA_TCD0.CSR[DONE])


In [5]:
# Read serialized TCD protocol buffer message for DMA channel 0 from device.
serialized_tcd0 = proxy.read_dma_TCD(0)
# Deserialize message into Python Protocol Buffer message.
tcd0 = dma.TCD.FromString(serialized_tcd0.tostring())

# Verify DMA operation is complete
# See TCD Control and Status section for more info (21.3.29/424 in manual).
assert(tcd0.CSR.DONE)